﻿using Cofe.Core.Dynamic;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Utils;
using System.Reflection;
using System.ComponentModel.Composition;
using Cofe.Core.Service;

namespace Cofe.Core.Implements
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class EntryConstructor : IEntryConstructor
    {
        #region Constructor

        public EntryConstructor(IDynamicPropertyInterfaceProvider[] providers,
            ISecondaryPropertyProviderFactory[] secondaryPropertyProviderFactories,
            IPropertyDictionary propertyDictionary,
            IPathParser[] pathParsers)
        {
            _providers = providers;
            _secondaryPropertyProviderFactories = secondaryPropertyProviderFactories;
            _propertyDictionary = propertyDictionary;
            _pathParsers = pathParsers;
        }

        public EntryConstructor()
        {

        }


        #endregion

        #region Methods



        private ICofeSystemInfo constructEntry(Type outputType, IPropertyHost propertyHost)
        {
            var retVal = CofeServices.PropertyInterfaceManager.CastAs(propertyHost, outputType) as ICofeSystemInfo;
            if (retVal != null)
            {
                //CofeServices.Logger.Log(String.Format("EC: Casting {0} to {1}",
                //      propertyHost.TryGetValue(CofeProperties.FullParseName, ""), outputType.Name), Category.Info , Priority.None);
            }
            else
            {
                CofeServices.Logger.Log(String.Format("EC: Cannot cast {0} to {1}",
                propertyHost.TryGetValue(CofeProperties.FullParseName, ""), outputType.Name), Category.Warn, Priority.Medium);
            }
            return retVal;
        }

        public IPropertyHost ConstructPropertyHost(IPropertyProvider primaryPropertyProvider)
        {
            if (primaryPropertyProvider == null)
                throw new ArgumentNullException("PrimaryPropertyProvider");
            return new PropertyHost(primaryPropertyProvider, ServiceLocater);
        }

        public IPropertyHost ConstructPropertyHost()
        {            
                return new PropertyHost(CofeServices.PropertyCacheFactory.CreatePropertyCache(), ServiceLocater);         
        }

        public async Task<ICofeSystemInfo> ConstructEntryAsync(IPropertyHost propertyHost)
        {
            Type outputType = typeof(ICofeSystemInfo);
            if (propertyHost.SupportedDefinitions.IsPropertySupported(CofeProperties.DefaultInterfaceType))
                outputType =
                    (await propertyHost.Behaviors.GetPropertyAsync(CofeProperties.DefaultInterfaceType)).Value as Type;

            if (outputType == null)
                throw new NotSupportedException();

            return constructEntry(outputType, propertyHost);
        }

        public Task<T> ConstructEntryAsync<T>(IPropertyHost propertyHost) where T : ICofeSystemInfo
        {
            return Task.FromResult<T>(ConstructEntry<T>(propertyHost));
        }

        public Task<ICofeSystemInfo> ConstructEntryAsync(IPropertyHost propertyHost, Type type)
        {
            return Task.FromResult(ConstructEntry(propertyHost, type));
        }



        public ICofeSystemInfo ConstructEntry(IPropertyHost propertyHost)
        {
            var retVal = AsyncUtils.RunSync(() => ConstructEntryAsync(propertyHost));
            if (retVal == null)
                throw new Exception("Entry Construct failed.");
            return retVal;
        }

        public T ConstructEntry<T>(IPropertyHost propertyHost) where T : ICofeSystemInfo
        {
            if (propertyHost == null)
                throw new ArgumentNullException("PropertyHost");

            Type outputType = typeof(T);
            var retVal = constructEntry(outputType, propertyHost);
            if (retVal is T)
                return (T)retVal;

            return default(T);
        }

        public ICofeSystemInfo ConstructEntry(IPropertyHost propertyHost, Type outputType)
        {
            if (propertyHost != null)
            {
                var retVal = constructEntry(outputType, propertyHost);
                return retVal;
            }
            return null;
        }

        public async Task<IPropertyProvider> RegenerateEntryPrimaryPP(IPropertyCache cache)
        {
            foreach (var regen in EntryRegenerators)
            {
                var retVal = await regen.RegenerateAsync(cache);
                if (retVal != null)
                    return retVal;
            }
            return null;
        }


        #endregion

        #region Data

        private ISecondaryPropertyProviderFactory[] _secondaryPropertyProviderFactories = null;
        private IPropertyDictionary _propertyDictionary = null;
        private IPathParser[] _pathParsers = null;
        private IDynamicPropertyInterfaceProvider[] _providers = null;
        private IServiceLocater _serviceLocater = null;
        private IEntryPrimaryPPRegenerator[] _entryRegenerators = null;



        #endregion

        #region Public Properties

        public IServiceLocater ServiceLocater
        {
            get { return _serviceLocater ?? CofeServices.ServiceLocater; }
            set { _serviceLocater = value; }
        }

        public IEntryPrimaryPPRegenerator[] EntryRegenerators
        {
            get { return _entryRegenerators ?? (_entryRegenerators = CofeServices.ServiceLocater.FindAllServices<IEntryPrimaryPPRegenerator>().ToArray()); }
            set { _entryRegenerators = value; }
        }

        public ISecondaryPropertyProviderFactory[] SecondaryPropertyProviderFactories
        {
            get { return _secondaryPropertyProviderFactories ?? (_secondaryPropertyProviderFactories = CofeServices.ServiceLocater.FindAllServices<ISecondaryPropertyProviderFactory>().ToArray()); }
            set { _secondaryPropertyProviderFactories = value; }
        }

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }

        public IPathParser[] PathParsers
        {
            get { return _pathParsers ?? CofeServices.ServiceLocater.FindAllServices<IPathParser>().ToArray(); }
            set { _pathParsers = value; }
        }

        public IDynamicPropertyInterfaceProvider[] InterfaceProviders
        {
            get { return _providers ?? CofeServices.ServiceLocater.FindAllServices<IDynamicPropertyInterfaceProvider>().ToArray(); }
            set { _providers = value; }
        }
        #endregion















    }
}
